<template>
    <div class="containers" ref="content"  :style="{height: panelHeight}">
        <template>
            <!-- 画图界面 -->
            <div class="canvas" ref="canvas">
            </div>
            <button class="save_xml" ref="saveBpmn" :disabled="saveXmlInfo.type">保存</button>
            <!-- 右侧属性面板 -->
            <div id="js-properties-panel" class="panel"></div>
        </template>
        <!-- 
            保存弹出框
         -->
        
        <a-modal
            title="模型信息"
            :visible="dialogFormVisible"
            @ok="handleSaveClick('module')"
            @cancel="dialogFormVisible = false"
            >
            <a-form-model :model="module" :rules="rules" ref="module">
                <a-form-model-item label="模型名字" prop="name" style="margin-bottom:0;">
                    <a-input  v-model="module.name" />
                </a-form-model-item>
                <a-form-model-item label="key" prop="key" style="margin-bottom:0;">
                    <a-input  v-model="module.key"/>
                </a-form-model-item>
                <a-form-model-item label="描述" prop="desc" style="margin-bottom:0;">
                    <a-textarea :rows="2" placeholder="请输入描述" v-model="module.desc"></a-textarea>
                </a-form-model-item>
            </a-form-model>
        </a-modal>
    </div>
</template>

<script>
    // 引入相关的依赖
    import BpmnModeler from 'bpmn-js/lib/Modeler'
    import propertiesPanelModule from '../../workflow/properties-panel'
    import propertiesProviderModule from '../../workflow/properties-panel/provider/activiti'
    import customTranslate from '../../workflow/customTranslate/customTranslate'
    import customControlsModule from '../../workflow/customControls'
    import activitiModdleDescriptor from '../../mock/activiti.json'
    import {xmlStr} from '../../mock/xmlStr'
    import {mapState, mapMutations, mapActions} from 'vuex'
    import {removeStore} from '../../utils/storage'

    export default {
        name: '',
        mounted() {
            this.init();
        },
        created(){
            this.getUserInfo();
        },
        data() {
            return {
                // bpmn建模器
                bpmnModeler: null,
                container: null,
                // 画布
                canvas: null,
                // 初始流程
                defaultXmlStr: xmlStr,
                option: null,
                saveXmlInfo: {
                    type: true,
                    xml: ''
                },
                module: {},
                // 弹出框是否显示
                dialogFormVisible: false,
                // 弹出框表单验证
                rules:{
                    name: [
                        { required: true, message: '请输入活动名称', trigger: 'blur' }
                    ],
                    key: [
                        { required: true, message: '请输入key', trigger: 'blur' }
                    ],
                    desc: [
                        { required: true, message: '请输入描述', trigger: 'blur' }
                    ]
                },
            }
        },
        methods: {
            // 调用vuex中的获取用户列表和角色权限方法
            ...mapActions(['getUserInfo']),
            // 初始化
            async init() {
                this.$nextTick(() => { // 等待 DOM 更新之后再对工作流进行初始化
                    this.initBpmn();
                })
            },
            initBpmn() {
                // 这里是初始化工作流的代码
                let customTranslateModule = {
                    translate: ['value', customTranslate]
                };
                // 获取到属性ref为“content”的dom节点
                this.container = this.$refs.content
                // 获取到属性ref为“canvas”的dom节点
                const canvas = this.$refs.canvas
                // 建模
                this.bpmnModeler = new BpmnModeler({
                    container: canvas,
                    //添加控制板
                    propertiesPanel: {
                        parent: '#js-properties-panel'
                    },
                    additionalModules: [
                        // 左边工具栏以及节点
                        propertiesProviderModule,
                        // 右边的工具栏
                        propertiesPanelModule,
                        // 汉化
                        customTranslateModule,
                        customControlsModule
                    ],
                    //模块拓展，拓展activiti的描述
                    moddleExtensions: {
                        activiti: activitiModdleDescriptor
                    }
                })
                // 在初始流程在画布中显示出来
                this.transformCanvas(this.defaultXmlStr);
            },
            transformCanvas(bpmnXmlStr) {
                 // 将字符串转换成图显示出来
                this.bpmnModeler.importXML(bpmnXmlStr, (err) => {
                    if (err) {
                        console.error(err)
                    } else {
                        // 这里是成功之后的回调, 可以在这里做一系列事情
                        this.success()
                    }
                    // 让图能自适应屏幕
                    var canvas = this.bpmnModeler.get('canvas')
                    canvas.zoom('fit-viewport')
                })
            },
            success() {
                this.addEventBusListener();
                this.addModelerListener();
            },
            // 为userTask 用户节点属性中候选用户和候选组的多选
            setUserTask(shape){
                // 节点属性候选用户和候选组是否有值，如果没有则默认为空数组
                let taskInfo = {
                    userOptions: shape.businessObject.candidateUsers || [], // 候选用户
                    rolaOptions: shape.businessObject.candidateGroups || [] // 候选组
                }
                const modeling = this.bpmnModeler.get('modeling');
                handleAddFunClick('activiti-candidateUsersSelect-select', taskInfo.userOptions, 'candidateUsers')
                handleAddFunClick('activiti-candidateGroupsSelect-select', taskInfo.rolaOptions, 'candidateGroups')
                function handleAddFunClick(id, data, type){
                    if(document.getElementById(id)){
                        document.getElementById(id).addEventListener('change',function(){
                            // 查找当前select的value值是否在data中存在
                            const val = data.indexOf(this.options[this.selectedIndex].value);
                            if(val == -1){
                                // val为-1时，代表data中没有当前值，需要push到data中
                                data.push(this.options[this.selectedIndex].value);
                                if(type === 'candidateUsers'){
                                    // 更新节点右侧的属性值
                                    modeling.updateProperties(shape, {
                                        candidateUsers: data
                                    })
                                }else if(type === 'candidateGroups'){
                                    modeling.updateProperties(shape, {
                                        candidateGroups: data
                                    })
                                }
                            }
                        })
                    }
                }
            },
            // 监听 modeler 并绑定事件
            addModelerListener() {
                const bpmnjs = this.bpmnModeler;
                var elementRegistry = bpmnjs.get('elementRegistry');
                // 元素点击和创建的方法
                const eventTypes = ['create.cleanup', 'element.click'];
                eventTypes.forEach(item=>{
                    // 触发对应的方法
                    this.bpmnModeler.on(item, e => {
                        // 获取节点属性
                        var shape = e.element ? elementRegistry.get(e.element.id) : e.shape;
                        // 如果当前节点的类型为userTask,则调用setUserTask方法
                        if (shape.type === 'bpmn:UserTask') {
                            this.setUserTask(shape);
                        }
                    })
                })
            },
            // 监听 element 
            addEventBusListener() {
                const that = this;
                const saveBpmn = this.$refs.saveBpmn;
                const eventBus = this.bpmnModeler.get('eventBus');
                const modeling = this.bpmnModeler.get('modeling');
                // 它会打印出该节点的Shape信息和DOM信息等, 但我们可能只关注于Shape信息(也就是该节点的id、type等等信息)
                const elementRegistry = this.bpmnModeler.get('elementRegistry');
                // element.changed 当元素发生改变的时候(包括新增、移动、删除元素)
                eventBus.on('element.changed', e=>{
                    if (e.element.type == 'bpmn:Process'){
                        // 当前修改元素如果为画布时，将id和name赋值给弹出框的key和name
                        this.$set(this.module,'key', e.element.id)
                        this.$set(this.module,'name', e.element.businessObject.name)
                    }
                })
                // 给图绑定事件，当图有发生改变就会触发这个事件
                this.bpmnModeler.on('commandStack.changed', function () {
                    // 保存按钮的disabled的状态
                    that.saveXmlInfo.type = false;
                    that.saveDiagram(function (err, xml) {
                        that.saveXmlInfo.xml = err ? null : xml
                        saveBpmn.addEventListener('click', () => {
                            // 显示弹出框
                            that.dialogFormVisible = true;
                        })
                    })
                })
            },
            // 下载为bpmn格式,done是个函数，调用的时候传入的
            saveDiagram(done) {
                this.bpmnModeler.saveXML({format: true}, function (err, xml) {
                    done(err, xml)
                })
            },
            handleSaveClick(formName){
                this.$refs[formName].validate((valid) => {
                    if (valid) {
                        // 将xml从saveXmlInfo中解构出来
                        const { xml } = this.saveXmlInfo;
                        // 使用xml创建一个新的文件
                        let xmlFile = new File([xml], 'diagram.bpmn');
                        let xmlFormData = new FormData();
                        // 将文件和模型对象添加到fromData中
                        xmlFormData.append('file', xmlFile);
                        xmlFormData.append('model', JSON.stringify(this.module));
                        this.$api.flowChartService.create(xmlFormData).then(res=>{
                            if(res.returnCode == 0){
                                this.$message.success(res.returnMessage)
                                this.dialogFormVisible = false;
                            }else{
                                this.$message.error(res.returnMessage)
                            }
                        }).catch(err=>{
                            this.$message.error(err)
                        })
                    }else{
                        this.$message.warning('警告,验证失败,请重新填写')
                        return false;
                    }
                });
            }
        },
        computed: {
            ...mapState(['getHost', 'panelHeight'])
        },
        beforeDestroy () {
            removeStore('getUserInfo')
        }
    }
</script>

<style scoped>
    .containers {
        background-color: #ffffff;
        width: 100%;
        position: relative;
    }

    .canvas {
        width: 100%;
        height: 100%;
    }

    .panel {
        position: absolute;
        right: 0;
        top: 0;
        width: 300px;
        height: 100%;
        overflow: scroll;
    }

    .loading {
        font-size: 30px;
        margin: 100px 0;
    }
    .save_xml{
        float: left;
        margin: 10px 0 0 20px;
        width: 94px;
        padding: 8px 5px;
        background: #fafafa;
        border: 1px solid #ccc;
        color: #000;
        position: absolute;
        bottom: 10px;
        left: 0;        
    }
    .save_xml:disabled{
        background: #fff;
        color: #ccc;
        border: 1px solid #ddd;
    }
</style>
